]> dgit.raspbian.org Git - gnome-shell.git/commitdiff
shell-entry: Add API to support entry context menus
authorFlorian Müllner <fmuellner@gnome.org>
Wed, 14 Sep 2011 16:47:09 +0000 (18:47 +0200)
committerFlorian Müllner <fmuellner@gnome.org>
Mon, 17 Oct 2011 13:29:32 +0000 (15:29 +0200)
Add addContextMenu() to support context menus on right-click/long-press.
Depending on the parameters passed, the context menu only contains
"Copy"/"Paste" actions or an additional "Show/Hide Text" toggle action
for password entries.

https://bugzilla.gnome.org/show_bug.cgi?id=659275

js/Makefile.am
js/ui/shellEntry.js [new file with mode: 0644]

index 7c726374cef2d3416f3cebb5aac0cd4be5d56e60..58e04892b891e1b59b501e53679d48e01b51bf37 100644 (file)
@@ -46,6 +46,7 @@ nobase_dist_js_DATA =         \
        ui/messageTray.js       \
        ui/modalDialog.js       \
        ui/networkAgent.js      \
+       ui/shellEntry.js        \
        ui/shellMountOperation.js \
        ui/notificationDaemon.js \
        ui/overview.js          \
diff --git a/js/ui/shellEntry.js b/js/ui/shellEntry.js
new file mode 100644 (file)
index 0000000..1dc0088
--- /dev/null
@@ -0,0 +1,173 @@
+const Clutter = imports.gi.Clutter;
+const Gtk = imports.gi.Gtk;
+const Lang = imports.lang;
+const St = imports.gi.St;
+
+const Main = imports.ui.main;
+const Params = imports.misc.params;
+const PopupMenu = imports.ui.popupMenu;
+
+
+function _EntryMenu(entry, params) {
+    this._init(entry, params);
+};
+
+_EntryMenu.prototype = {
+    __proto__: PopupMenu.PopupMenu.prototype,
+
+    _init: function(entry, params) {
+        params = Params.parse (params, { isPassword: false });
+
+        PopupMenu.PopupMenu.prototype._init.call(this, entry, 0, St.Side.TOP);
+
+        this.actor.add_style_class_name('entry-context-menu');
+
+        this._entry = entry;
+        this._clipboard = St.Clipboard.get_default();
+
+        // Populate menu
+        let item;
+        item = new PopupMenu.PopupMenuItem(_("Copy"));
+        item.connect('activate', Lang.bind(this, this._onCopyActivated));
+        this.addMenuItem(item);
+        this._copyItem = item;
+
+        item = new PopupMenu.PopupMenuItem(_("Paste"));
+        item.connect('activate', Lang.bind(this, this._onPasteActivated));
+        this.addMenuItem(item);
+        this._pasteItem = item;
+
+        this._passwordItem = null;
+        if (params.isPassword) {
+            item = new PopupMenu.PopupMenuItem('');
+            item.connect('activate', Lang.bind(this,
+                                               this._onPasswordActivated));
+            this.addMenuItem(item);
+            this._passwordItem = item;
+        }
+
+        Main.uiGroup.add_actor(this.actor);
+        this.actor.hide();
+    },
+
+    open: function() {
+        this._updatePasteItem();
+        this._updateCopyItem();
+        if (this._passwordItem)
+            this._updatePasswordItem();
+
+        let direction = Gtk.DirectionType.TAB_FORWARD;
+        if (!this.actor.navigate_focus(null, direction, false))
+            this.actor.grab_key_focus();
+
+        PopupMenu.PopupMenu.prototype.open.call(this);
+    },
+
+    _updateCopyItem: function() {
+        let selection = this._entry.clutter_text.get_selection();
+        this._copyItem.setSensitive(selection && selection != '');
+    },
+
+    _updatePasteItem: function() {
+        this._clipboard.get_text(Lang.bind(this,
+            function(clipboard, text) {
+                this._pasteItem.setSensitive(text && text != '');
+            }));
+    },
+
+    _updatePasswordItem: function() {
+        let textHidden = (this._entry.clutter_text.password_char);
+        if (textHidden)
+            this._passwordItem.label.set_text(_("Show Text"));
+        else
+            this._passwordItem.label.set_text(_("Hide Text"));
+    },
+
+    _onCopyActivated: function() {
+        let selection = this._entry.clutter_text.get_selection();
+        this._clipboard.set_text(selection);
+    },
+
+    _onPasteActivated: function() {
+        this._clipboard.get_text(Lang.bind(this,
+            function(clipboard, text) {
+                if (!text)
+                    return;
+                this._entry.clutter_text.delete_selection();
+                let pos = this._entry.clutter_text.get_cursor_position();
+                this._entry.clutter_text.insert_text(text, pos);
+            }));
+    },
+
+    _onPasswordActivated: function() {
+        let visible = !!(this._entry.clutter_text.password_char);
+        this._entry.clutter_text.set_password_char(visible ? '' : '\u25cf');
+    }
+};
+
+
+function _setMenuAlignment(entry, stageX) {
+    let [success, entryX, entryY] = entry.transform_stage_point(stageX, 0);
+    if (success)
+        entry._menu.setSourceAlignment(entryX / entry.width);
+};
+
+function _onClicked(action, actor) {
+    let entry = actor._menu ? actor : actor.get_parent();
+
+    if (entry._menu.isOpen) {
+        entry._menu.close();
+    } else if (action.get_button() == 3) {
+        let [stageX, stageY] = action.get_coords();
+        _setMenuAlignment(entry, stageX);
+        entry._menu.open();
+    }
+};
+
+function _onLongPress(action, actor, state) {
+    let entry = actor._menu ? actor : actor.get_parent();
+
+    if (state == Clutter.LongPressState.QUERY)
+        return action.get_button() == 1 && !entry._menu.isOpen;
+
+    if (state == Clutter.LongPressState.ACTIVATE) {
+        let [stageX, stageY] = action.get_coords();
+        _setMenuAlignment(entry, stageX);
+        entry._menu.open();
+    }
+    return false;
+};
+
+function _onPopup(actor) {
+    let entry = actor._menu ? actor : actor.get_parent();
+    let [success, textX, textY, lineHeight] = entry.clutter_text.position_to_coords(-1);
+    if (success)
+        entry._menu.setSourceAlignment(textX / entry.width);
+    entry._menu.open();
+};
+
+function addContextMenu(entry, params) {
+    if (entry._menu)
+        return;
+
+    entry._menu = new _EntryMenu(entry, params);
+    entry._menuManager = new PopupMenu.PopupMenuManager({ actor: entry });
+    entry._menuManager.addMenu(entry._menu);
+
+    let clickAction;
+
+    // Add a click action to both the entry and its clutter_text; the former
+    // so padding is included in the clickable area, the latter because the
+    // event processing of ClutterText prevents event-bubbling.
+    clickAction = new Clutter.ClickAction();
+    clickAction.connect('clicked', _onClicked);
+    clickAction.connect('long-press', _onLongPress);
+    entry.clutter_text.add_action(clickAction);
+
+    clickAction = new Clutter.ClickAction();
+    clickAction.connect('clicked', _onClicked);
+    clickAction.connect('long-press', _onLongPress);
+    entry.add_action(clickAction);
+
+    entry.connect('popup-menu', _onPopup);
+}